﻿namespace Pyramid.Hardware
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Pyramid.Hardware.Operations;

    public class Alu
    {
        public Alu(IEnumerable<ILine> busA, IEnumerable<ILine> busB, IEnumerable<ILine> controlLines, IEnumerable<IOperationFactory> operationFactories)
        {
            if (busA.Count() != busB.Count())
            {
                throw new ArgumentException("Bus sizes must match.");
            }

            int maxOperations = (int)Math.Pow(3, controlLines.Count());
            int operationsCount = operationFactories.Count();

            if (operationsCount > maxOperations)
            {
                throw new ArgumentException("Not enough control lines for given operations.");
            }

            operationFactories = operationFactories.Concat(Enumerable.Repeat<IOperationFactory>(new BlankOperationFactory(), maxOperations - operationsCount));

            var operations = operationFactories.Select(factory => factory.Create(busA, busB));

            //var multiplexerLayers = new List<IEnumerable<IEnumerable<ILine>>>();
            //multiplexerLayers.Add(operations.Select(operation => operation.OutputBus));

            //for (int layerIndex = 0; layerIndex < controlLines.Count(); layerIndex++)
            //{
            //    var layer = new List<BusMultiplexer>();
            //    int multiplexerCount = (int)Math.Pow(3, controlLines.Count() - (layerIndex + 1));

            //    for (int multiplexerIndex = 0; multiplexerIndex < multiplexerCount; multiplexerIndex++)
            //    {
            //        layer.Add(new BusMultiplexer(controlLines.ElementAt(layerIndex), multiplexerLayers.Last().Skip(multiplexerIndex * 3).Take(3)));
            //    }

            //    multiplexerLayers.Add(layer.Select(multiplexer => multiplexer.OutputBus));
            //}

            var outputMultiplexer = new BusMultiplexer(controlLines, operations.Select(operation => operation.OutputBus));

            this.OutputBus = outputMultiplexer.OutputBus;
        }

        public IEnumerable<ILine> OutputBus
        {
            get;
            private set;
        }
    }
}
